/**
 * 
 */
package pers.vic.test.url;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import pers.vic.boot.util.CommonUtils;

/**
 * @description: 自定义rRequestMappingHandlerMapping:对于部分@PathVariable 的requestMapping, 根据name和当前url匹配,省去循环遍历全部的requestMethod
 * @author Vic.xu
 * @date: 2020年5月22日下午1:55:01
 */
public class CustomerRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
	/*
	 * 使用方法
	  SpringMVC中:
	  <bean name="handlerMapping" class="xxx.xxx.CustomerRequestMappingHandlerMapping">
	  	<property name="names">
        <set>
            <value>set1</value>
            <value>set2</value>
        </set>	
	  </bean>
	  SpringBoot中:
	  @Configuration
	  public class CustomerUrlMartchConfig extends WebMvcConfigurationSupport{
			@Override
			protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
				return new CustomerRequestMappingHandlerMapping(...);
			}
      }     
	 */
	
	/**
	 * 需根据URL直接匹配RequestMapping 的 RequestMapping name集合(全部小写) 如果URL包含这些名称的关键词,
	 * 则先尝试当过这些名称去匹配RequestMethod
	 */
	public Set<String> names = new HashSet<String>();
	
	
	public CustomerRequestMappingHandlerMapping() {}
	public CustomerRequestMappingHandlerMapping(Set<String> names) {
		this.names = names;
	}
	
	@Override
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		System.out.println("当前系统的HandlerMethod的总数为:" + mappingLookup.size());
	}
	

	@Override
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		HandlerMethod handlerMethod = lookupHandlerMethodHere(lookupPath, request);
		// 如果自己的逻辑找不到 则走默认的
		if (handlerMethod == null) {
			handlerMethod = super.lookupHandlerMethod(lookupPath, request);
		}
		return handlerMethod;
	}

	// 映射map
	private final Map<HandlerMethod, RequestMappingInfo> mappingLookup = new LinkedHashMap<HandlerMethod, RequestMappingInfo>();

	/**
	 * 因为RESTful接口存在@PathVariable，我们还需要调用handleMatch方法来将HTTP请求的path解析成参数。然而这个方法需要的参数是RequestMappingInfo，并不是HandlerMethod，SpringMVC也没有提供任何映射。
	 * 做法：重写registerHandlerMethod方法，再初始化的时候构建一个从HandlerMethod—>RequestMappingInfo的反向映射。
	 */
	@Override
	protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		mappingLookup.put(handlerMethod, mapping);
		super.registerHandlerMethod(handler, method, mapping);
	}

	


	public String getRequestNameByUrl(HttpServletRequest request) {
		String url = CommonUtils.getRequestUrl(request).toLowerCase();
		String result = "";
		for (String name : names) {
			if (url.contains(name) && name.length() > result.length()) {
				result = name;
			}
		}
		return result;
	}
	/*
	 public static String getRequestUrl(HttpServletRequest request) {
		String contextPath = request.getContextPath();
		String requestURI = request.getRequestURI();
		int i = requestURI.indexOf("?");
		if (i < 0) {
			i = requestURI.length();
		}
		return requestURI.substring(contextPath.length(), i);
	}
	 */

	/**
	 * 自己的查找逻辑，根据从请求头中获取服务名servicename，进行匹配查找
	 * 
	 * @param lookupPath
	 * @param request
	 * @return
	 */
	private HandlerMethod lookupHandlerMethodHere(String lookupPath, HttpServletRequest request) {
		String name = getRequestNameByUrl(request);
		if (!StringUtils.isEmpty(name)) {
			List<HandlerMethod> methodList = this.getHandlerMethodsForMappingName(name);
			if (methodList == null) {
				return null;
			}
			if (methodList.size() > 0) {
				HandlerMethod handlerMethod = methodList.get(0);
				RequestMappingInfo requestMappingInfo = mappingLookup.get(handlerMethod);
				handleMatch(requestMappingInfo, lookupPath, request);
				return handlerMethod;
			}
		}
		return null;
	}

}
